package com.cooperate.fly.service.buildDB.impl;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.cooperate.fly.bo.Field;
import com.cooperate.fly.bo.Schema;
import com.cooperate.fly.bo.TMatrixX;
import com.cooperate.fly.bo.TMatrixY;
import com.cooperate.fly.bo.TMatrixZWithBLOBs;
import com.cooperate.fly.mapper.FieldMapper;
import com.cooperate.fly.mapper.SchemaMapper;
import com.cooperate.fly.mapper.TMatrixXMapper;
import com.cooperate.fly.mapper.TMatrixYMapper;
import com.cooperate.fly.mapper.TMatrixZMapper;
import com.cooperate.fly.mapper.TableMapper;
import com.cooperate.fly.service.buildDB.BuildFieldService;
import com.cooperate.fly.web.Result;
import com.cooperate.fly.web.WebFrontHelper;
import com.google.gson.Gson;

import net.sf.json.JSONArray;

@Service("build_field")
public class BuildFieldServiceImpl implements BuildFieldService {

	@Autowired
	private FieldMapper fieldMapper;

	@Autowired
	private SchemaMapper schemaMapper;

	@Autowired
	private TableMapper tableMapper;
	@Autowired
	private TMatrixXMapper tMatrixXMapper;

	@Autowired
	private TMatrixYMapper tMatrixYMapper;

	@Autowired
	private TMatrixZMapper tMatrixZMapper;

	@Value("#{propertiesReader['create_success']}")
	private String createSuccess;

	@Value("#{propertiesReader['create_fail']}")
	private String createFail;

	@Value("#{propertiesReader['create_same_name_node']}")
	private String createSameNameNode;

	@Value("#{propertiesReader['field_catalog_type']}")
	private int fieldCatalogType;

	@Value("#{propertiesReader['url_split_symbol']}")
	public String urlSplitSymbol;

	@Value("#{propertiesReader['field_root_node_id']}")
	public int fieldRootNodeId;

	@Value("#{propertiesReader['mysql_schema_name']}")
	public String mysqlSchemaName;

	@Resource
	private WebFrontHelper webFrontHelper;

	public static int fieldCount = 0;

	private static final Logger logger = LoggerFactory
			.getLogger(BuildFieldServiceImpl.class);

	@Override
	public Result createFieldNode(Field field) {
		Result result = new Result();
		result.setSuccessful(false);
		result.setMessage(createFail);
		if (!checkFieldName(field)) {
			result.setSuccessful(false);
			result.setMessage(createSameNameNode);
			return result;
		}
		// 在节点所属于的schema database中获得最大的order进行+1
		// 将数据库中最大节点order标号放在对应数据库的childCount的字段中
		Schema schema = schemaMapper.selectByPrimaryKey(field.getSchemaId());
		if (schema == null) {
			return result;
		}
		if (schema.getChildCount() != null) {
			field.setOrder(schema.getChildCount() + 1);
			schema.setChildCount(schema.getChildCount() + 1);
		} else {
			field.setOrder(1);
			schema.setChildCount(1);
		}
		schemaMapper.updateByPrimaryKey(schema);
		int res = fieldMapper.insert(field);
		Integer fid = field.getId();
		if ((res == 1) && (fid != null)) {
			Field parentField = fieldMapper.selectByPrimaryKey(field
					.getParentId());
			String path = null;
			if (parentField != null) {
				path = parentField.getPath();
				path += "/" + fid;
			} else {
				path = "/" + fid;
			}
			fieldMapper.updatePathById(path, fid);
			result.setMessage(createSuccess);
			result.setData(field.getId());
			result.setSuccessful(true);
		}
		return result;
	}

	@Override
	public Result create3Matrix(int fid, String xName, String xTitle,
			String yName, String yTitle, String zName) {
		TMatrixX tMatrixX = new TMatrixX();
		tMatrixX.setFid(fid);
		tMatrixX.setName(xName);
		tMatrixX.setValue(xTitle);
		TMatrixY tMatrixY = new TMatrixY();
		tMatrixY.setFid(fid);
		tMatrixY.setName(yName);
		tMatrixY.setValue(yTitle);
		TMatrixZWithBLOBs tMatrixZ = new TMatrixZWithBLOBs();
		tMatrixZ.setFid(fid);
		tMatrixZ.setName(zName);
		int res = 0;
		res += tMatrixXMapper.insert(tMatrixX);
		res += tMatrixYMapper.insert(tMatrixY);
		res += tMatrixZMapper.insert(tMatrixZ);
		Result result = new Result();
		if (res == 3) {
			result.setSuccessful(true);
		} else {
			result.setSuccessful(false);
		}
		return result;
	}

	@Override
	public Result get3MatrixInfo(int id) {
		Result result = new Result();
		return result;
	}

	@Override
	public Result deleteFieldNode(Field field) {
		return deleteFieldNodeById(field.getId());
	}

	@Override
	public Result deleteFieldNodeById(int id) {
		Result result = new Result();
		ArrayList<Field> sumFields = new ArrayList<Field>();
		// delete from the root node
		if (id == fieldRootNodeId) {
			ArrayList<Field> rootFileds = new ArrayList<Field>();
			rootFileds = fieldMapper.selectByParentId(id);
			for (Field field2 : rootFileds)
				getSonFields(field2, sumFields);
			sumFields.addAll(rootFileds);
		} else {
			Field field = fieldMapper.selectByPrimaryKey(id);
			getSonFields(field, sumFields);
			sumFields.add(field);
		}
		int count = 0;
		for (Field field1 : sumFields) {
			int res = fieldMapper.deleteByPrimaryKey(field1.getId());
			// int
			// tableNum=tableMapper.existTable("table_"+field1.getSchemaId());
			// int
			// tableNum2=tableMapper.checkTableExist("table_"+field1.getSchemaId());
			// if(tableNum==1) {
			// if(tableMapper.selectByFieldId("table_"+field1.getSchemaId(),field1.getId())>0)
			// {
			try {
				tableMapper.deleteByFieldId("table_" + field1.getSchemaId(),
						field1.getId());
			} catch (Exception e) {

			}
			// }
			// }
			if (res == 1) {
				count++;
			}
		}
		if (count == sumFields.size())
			result.setMessage(createSuccess);
		else {
			result.setMessage(createFail);
			result.setSuccessful(false);
		}
		return result;
	}

	@Override
	public Result updateFieldNode(Field field) {
		Result result = new Result();
		Field sourceField = fieldMapper.selectByPrimaryKey(field.getId());
		sourceField.setName(field.getName());
		sourceField.setRemark3(field.getRemark3());
		sourceField.setRemark1(field.getRemark1());
		if (!checkFieldName(sourceField)) {
			result.setSuccessful(false);
			result.setMessage(createSameNameNode);
			return result;
		}
		if (sourceField != null) {
			fieldMapper.updateByPrimaryKeyWithBLOBs(sourceField);
			result.setMessage(createSuccess);
		} else {
			result.setMessage(createFail);
			result.setSuccessful(false);
		}
		return result;
	}

	@Override
	public Result searchFieldNode(Field field) {
		return null;
	}

	@Override
	public Result searchFieldNodeById(int id) {
		return null;
	}

	@Override
	public String loadField(int databaseId) {
		ArrayList<Field> fields = fieldMapper.selectByDId(databaseId);
		Schema schema = schemaMapper.selectByPrimaryKey(databaseId);
		return webFrontHelper.buildFieldTree(fields, schema.getName());
	}

	@Override
	public Map<String, String> loadPartFile(int fieldId) {
		Field field = fieldMapper.selectByPrimaryKey(fieldId);
		ArrayList<Field> sumFields = new ArrayList<Field>();
		ArrayList<Schema> sumSchemas = new ArrayList<Schema>();
		Map<String, String> maps = new HashMap<String, String>();
		if (field != null) {
			getParentFields(field, sumFields);
			getSonFields(field, sumFields);
			sumFields.add(field);
			Schema schema = schemaMapper
					.selectByPrimaryKey(field.getSchemaId());
			getParentSchema(field, schema, sumSchemas);
			maps.put("fieldStr",
					webFrontHelper.buildFieldTree(sumFields, schema.getName()));
			maps.put("schemaStr", webFrontHelper.buildSchemaTree(sumSchemas));
			return maps;
		} else
			return null;
	}

	public Map<String, String> loadPartField(int fieldId) {
		Field field = fieldMapper.selectByPrimaryKey(fieldId);
		ArrayList<Field> sumFields = new ArrayList<Field>();
		ArrayList<Schema> sumSchemas = new ArrayList<Schema>();
		Map<String, String> maps = new HashMap<String, String>();
		if (field != null) {
			getParentFields(field, sumFields);
			getSonFields(field, sumFields);
			sumFields.add(field);
			Schema schema = schemaMapper
					.selectByPrimaryKey(field.getSchemaId());
			getParentSchema(field, schema, sumSchemas);
			maps.put("fieldStr", new Gson().toJson(sumFields));
			maps.put("schemaStr", new Gson().toJson(sumSchemas));
			return maps;
		} else
			return null;
	}

	@Override
	public String parseFieldUrl(String schemaPath, String fieldPath) {
		String[] schemaNames = schemaPath.split(urlSplitSymbol);
		int parentId = 0;
		Schema schema = null;
		for (int i = 0; i < schemaNames.length; i++) {
			String name = schemaNames[i];
			schema = schemaMapper.selectByNameAndParentId(name, parentId);
			parentId = schema.getId();
		}
		if (schema != null) {
			String[] fieldNames = fieldPath.split(urlSplitSymbol);
			parentId = fieldRootNodeId;
			Field field = null;
			Map<String, String> maps = null;
			for (int i = 0; i < fieldNames.length; i++) {
				String name = fieldNames[i];
				field = fieldMapper.selectByNameAndParentIdAndSchemaId(name,
						parentId, schema.getId());
				parentId = field.getId();
			}
			if (field != null) {
				maps = loadPartField(field.getId());
			}
			if (maps != null) {
				return maps.get("fieldStr") + "\t" + maps.get("schemaStr");
			}
		}
		return null;
	}

	@Override
	public Result getRemark1(int id) {
		Result result = new Result();
		Field field = fieldMapper.selectByPrimaryKey(id);
		if (field != null) {
			result.setData(field.getRemark1());
		} else {
			result.setSuccessful(false);
		}
		return result;
	}

	@Override
	public Result getRemark3(int id) {
		Result result = new Result();
		Field field = fieldMapper.selectByPrimaryKey(id);
		if (field != null) {
			result.setData(field.getRemark3());
		} else {
			result.setSuccessful(false);
		}
		return result;
	}

	// 递归循环该节点的子节点直到叶子节点的schema节点,不包含查询节点本身
	public void getSonFields(Field field, ArrayList<Field> fields) {
		if (field.getType() == fieldCatalogType) {
			ArrayList<Field> fieldList = fieldMapper.selectByParentId(field
					.getId());
			if (fieldList != null) {
				fields.addAll(fieldList);
				for (Field field1 : fieldList) {
					getSonFields(field1, fields);
				}
			}
		}
	}

	public void getParentFields(Field field, ArrayList<Field> fields) {
		if (field.getParentId() != fieldRootNodeId) {
			Field parentField = fieldMapper.selectByPrimaryKey(field.getId());
			fields.add(parentField);
		}
	}

	public void getParentSchema(Field field, Schema schema,
			ArrayList<Schema> schemas) {
		if (schema.getParentId() != 0) {
			Schema parentSchema = schemaMapper.selectByPrimaryKey(schema
					.getParentId());
			schemas.add(parentSchema);
		}
	}

	@Override
	public boolean checkFieldName(Field field) {
		if (field.getName() == null) {
			return false;
		}
		ArrayList<Field> fields = fieldMapper.selectByParentId(field
				.getParentId());
		logger.debug("field:id=" + field.getId() + " name=" + field.getName()
				+ " schemaId=" + field.getSchemaId());
		if (fields != null) {
			// update field
			if (field.getId() != null) {
				for (Field f : fields) {
					if (f.getName() == null && field.getName() != null
							|| f.getSchemaId() == null
							&& field.getSchemaId() != null) {
						continue;
					}
					if (f.getName().equals(field.getName())
							&& !(f.getId().equals(field.getId()))
							&& f.getSchemaId().equals(field.getSchemaId()))
						return false;
				}
			} else {
				for (Field f : fields) {
					logger.debug("f:id=" + f.getId() + " name=" + f.getName()
							+ " schemaId=" + f.getSchemaId());
					if (f.getName() == null && field.getName() != null
							|| f.getSchemaId() == null
							&& field.getSchemaId() != null) {
						continue;
					}
					if (f.getName().equals(field.getName())
							&& f.getSchemaId().equals(field.getSchemaId()))
						return false;
				}
			}
		}
		return true;
	}

	// download the data of the database
	@Override
	public File downloadFields(int databaseId, int fieldId) throws Exception {
		ArrayList<Field> fields = new ArrayList<Field>();
		if (fieldId == fieldRootNodeId) {
			fields = fieldMapper.selectByDId(databaseId);
		} else {
			Field temp = fieldMapper.selectByPrimaryKey(fieldId);
			getSonFields(temp, fields);
		}
		String data;
		if (fields != null) {
			data = new Gson().toJson(fields);
		} else {
			data = "[]";
		}
		String originPath = this.getClass().getResource("/").getPath();
		originPath = originPath.substring(0, originPath.indexOf("WEB-INF"));
		originPath = originPath + "download";
		String fileName = "database_" + databaseId + "_";
		String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
		fileName = fileName + time + ".json";
		File originFile = new File(originPath);
		if (!originFile.exists()) {
			originFile.mkdirs();
		}
		String filePath = originPath + File.separator + fileName;
		File sourceFile = new File(filePath);
		if (!sourceFile.exists()) {
			sourceFile.createNewFile();
		}
		FileOutputStream os = new FileOutputStream(filePath);
		BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(os,
				"UTF-8"));
		writer.write(data);
		writer.close();
		os.close();
		return sourceFile;
	}

	@Override
	@Transactional
	public Result freshData(String fieldsStr, int schemaId, int fieldId) {
		JSONArray jsonArray = JSONArray.fromObject(fieldsStr);
		Result result = new Result();
		result.setSuccessful(false);
		result.setMessage(createFail);
		result.setData(500);
		List<Field> list = JSONArray.toList(jsonArray, Field.class);
		int count = 0;
		if (fieldId == fieldRootNodeId) {
			fieldMapper.deleteBySchemaId(schemaId);// if runtime exception
													// happened, transaction
													// roll back
		} else {
			ArrayList<Field> deleteFields = new ArrayList<Field>();
			Field field = fieldMapper.selectByPrimaryKey(fieldId);
			getSonFields(field, deleteFields);
			if (deleteFields.size() > 0) {
				for (Field field1 : deleteFields) {
					fieldMapper.deleteByPrimaryKey(field1.getId());
				}
			}
		}
		count = reStructFields(list, schemaId, fieldId);
		if (count == list.size()) {
			result.setMessage(createSuccess);
			result.setSuccessful(true);
			result.setData(200);
			return result;
		} else {
			int i = 1 / 0;// make the runtime exception
		}
		return result;
	}

	private int reStructFields(List<Field> sourceFields, int sid, int fid) {
		HashMap<Integer, ArrayList<Field>> parentIdToSonIds = new HashMap<Integer, ArrayList<Field>>();
		HashMap<Integer, Field> idToField = new HashMap<Integer, Field>();
		for (Field field : sourceFields) {
			idToField.put(field.getId(), field);
			int pid = field.getParentId();
			if (parentIdToSonIds.get(pid) != null) {
				parentIdToSonIds.get(pid).add(field);
			} else {
				ArrayList<Field> sonFields = new ArrayList<Field>();
				sonFields.add(field);
				parentIdToSonIds.put(pid, sonFields);
			}
		}
		// 获得根节点的第一批子节点,即父节点的id不属于这个集合中。
		List<Field> sons = new ArrayList<Field>();
		for (Field field : sourceFields) {
			if (idToField.get(field.getParentId()) == null) {
				sons.add(field);
			}
		}

		int newPid = fid;
		int count = 0;
		// map key: Array0 oldPid, array1 newPid, List<Field> sons
		Queue<StructNode> queue = new LinkedList<StructNode>();
		if (sons.size() > 0) {
			StructNode node = new StructNode();
			node.setNewPid(newPid);
			node.setSons(sons);
			queue.add(node);
		}

		while (!queue.isEmpty()) {
			StructNode q = queue.remove();
			newPid = q.getNewPid();
			sons = q.getSons();
			for (Field field : sons) {
				int tempOid = field.getId();
				field.setSchemaId(sid);
				field.setParentId(newPid);
				field.setId(null);
				count += fieldMapper.insert(field);
				int tempNid = field.getId();
				StructNode tempNode = new StructNode();
				tempNode.setNewPid(tempNid);
				if (parentIdToSonIds.get(tempOid) != null) {
					tempNode.setSons(parentIdToSonIds.get(tempOid));
					queue.add(tempNode);
				}
			}
		}
		// fieldRecursion(parentIdToSonIds,0,0,sid);
		return count;
	}

	private void fieldRecursion(HashMap<Integer, ArrayList<Field>> fieldMap,
			int oldPid, int newPid, int sid) {
		ArrayList<Field> sons = fieldMap.get(oldPid);
		if (sons != null) {
			for (Field field : sons) {
				field.setSchemaId(sid);
				oldPid = field.getId();
				field.setId(null);
				field.setParentId(newPid);
				fieldMapper.insert(field);
				int tempId = field.getId();
				fieldRecursion(fieldMap, oldPid, tempId, sid);
			}
		} else
			return;
	}

	class StructNode {
		int newPid;

		public int getNewPid() {
			return newPid;
		}

		public void setNewPid(int newPid) {
			this.newPid = newPid;
		}

		public List<Field> getSons() {
			return sons;
		}

		public void setSons(List<Field> sons) {
			this.sons = sons;
		}

		List<Field> sons;
	}

	@Override
	public Result exchangeNode(int frontNodeId, int backNodeId) {
		Result result = new Result();
		result.setSuccessful(false);
		result.setMessage(createFail);
		Field frontField = fieldMapper.selectByPrimaryKey(frontNodeId);
		Field backField = fieldMapper.selectByPrimaryKey(backNodeId);
		if (frontField != null && backField != null
				&& (frontField.getParentId().equals(backField.getParentId()))) {
			if (frontField.getOrder() != null && backField.getOrder() != null) {
				int tempOrder = frontField.getOrder();
				frontField.setOrder(backField.getOrder());
				backField.setOrder(tempOrder);
				fieldMapper.updateByPrimaryKey(frontField);
				fieldMapper.updateByPrimaryKey(backField);
				result.setSuccessful(true);
				result.setMessage(createSuccess);
			}
		}
		return result;
	}
}
